/**
 * 弹框样式
 *
 * @param msg   内容
 * @param fn    回调方法
 * @param time  弹框存在时间
 */
function dialog(msg, fn = false, time = 1500) {
    let keyboard = {
        // 防止重复提交
        enterRepeat() {
            $(document).on('keydown keyup', function (e) {
                var Event = e || event;

                if (Event.keyCode == 13) {
                    return false;
                }
            })
        },

        // 关闭防止重复提交
        closeEnter() {
            $(document).unbind('keydown keyup');
        }
    };

    layui.use('layer', function () {
        let layer = layui.layer;

        layer.msg(msg, {
            time: time,
            success: function () {
                // 防止重复提交
                keyboard.enterRepeat();
            }
        }, function () {
            // 关闭防止重复提交
            keyboard.closeEnter();

            // 有回调方法直接执行
            if (fn) fn();
        });
    });
}

/**
 *
 * 请求 load 层
 *
 * @param request
 * @param isResetToken  是否需要替换 token
 */
function load(request, isResetToken = true) {
    let promise = {};

    layui.use('layer', () => {
        let load = layer.load(2);

        promise = new Promise((resolve, reject) => {
            request.then(res => {
                // 特殊错误码 处理
                if (res.code != 200) errorHandler(res.code);

                // 正常执行
                resolve(res);
            }).catch(res => reject(res)).finally(async () => {
                // 重新设置 token
                if (isResetToken) await resetToken();

                layer.close(load);
            });
        });
    });

    return promise;
}

/**
 * 重置 token 令牌
 */
function resetToken() {
    return new Promise(resolve => {
        import("./api/additional/auth.js").then(module => {
            module.exchangeToken({}).then(res => {
                $('meta[name="csrf-token"]', top.window.document).attr('content', res.data.token);

                resolve();
            });
        });
    });
}

/**
 * 特殊 错误码 处理
 *
 *
 * @param code
 */
function errorHandler(code) {
    import("./app.js").then(module => {
        if (module.rain.config.errors.includes(code)) parent.location.reload();
    });
}

/**
 * form 表单请求
 *
 * @param form      form 组件
 * @param event     监听事件名称
 * @param request   { request: '请求对象', id: '数据id' }
 * @param fn        回调执行方法
 * @param startFn   开始执行回调方法
 */
function formData(form, event, request, fn, startFn) {
    // 防止重复提交
    let lock = false;

    form.on(`submit(${event})`, (data) => {
        // 验证锁 并锁上
        if (lock) return false;
        lock = false;

        // 开始执行回调
        if (startFn) startFn(data);

        if (event == 'add') {
            // 添加
            request.request(data.field).then(res => {
                dialog(res.msg, () => {
                    if (fn) fn();

                    if (200 == res.code) {
                        closeCurrentPage();
                    } else {
                        // 请求失败，锁关闭，可重新提交
                        lock = false;
                    }
                });
            });
        } else if (event == 'edit') {
            // 修改
            request.request(request.id, data.field).then(res => {
                dialog(res.msg, () => {
                    if (fn) fn();

                    if (200 == res.code) {
                        closeCurrentPage();
                    } else {
                        // 请求失败，锁关闭，可重新提交
                        lock = false;
                    }
                });
            });
        }

        return false;
    });
}

/**
 * 当前端口 强制跳转 顶级窗口
 */
function isTopLocation() {
    if (top.location != self.location) top.location = self.location;
}

/**
 * 关闭当前窗口
 *
 * @param isReload  是否需要重载页面
 */
function closeCurrentPage(isReload = true) {
    xadmin.close();

    if (isReload) xadmin.father_reload();
}

/**
 * 判断是否为数字
 *
 * @param number
 * @returns {boolean}
 */
function isNumber(number) {
    let re = /^[0-9]+.?[0-9]*$/;

    if (!re.test(number)) return false;

    return true;
}

/**
 * input 输入只能为整数
 *
 * @param obj
 */
function onlyNumber(obj) {
    //先把非数字的都替换掉，除了数字和.和-号
    obj.value = obj.value.replace(/[^\d]/g, '');
}

/**
 * input 输入只能为金额
 *
 * @param obj
 */
function onlyPrice(obj) {
    //得到第一个字符是否为负号
    let t = obj.value.charAt(0);
    //先把非数字的都替换掉，除了数字和.和-号
    obj.value = obj.value.replace(/[^\d\.\-]/g, '');
    //前两位不能是0加数字
    obj.value = obj.value.replace(/^0\d[0-9]*/g, '');
    //必须保证第一个为数字而不是.
    obj.value = obj.value.replace(/^\./g, '');
    //保证只有出现一个.而没有多个.
    obj.value = obj.value.replace(/\.{2,}/g, '.');
    //保证.只出现一次，而不能出现两次以上
    obj.value = obj.value.replace('.', '$#$').replace(/\./g, '').replace('$#$', '.');
    //如果第一位是负号，则允许添加
    obj.value = obj.value.replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3');
}

/**
 * 局内添加表格元素
 *
 * @param params        tr 内容
 * @param id            id
 * @param inputName     input name名称
 * @param elem          tbody 元素
 */
function addTr(params, id, inputName, elem) {
    // 获取已存在的用户id
    let ids = [];
    [...$(`input[name^="${inputName}"]`)].forEach(item => {
        ids.push($(item).val());
    });

    // 验证是否重复添加
    if (ids.includes(id.toString())) return;

    // 添加一行数据
    let tr = '<tr>';

    // 行内参数
    for (let i in params) {
        tr += `<td>${params[i]}</td>`;
    }

    // 添加操作相关内容
    tr += `<td><a href="javascript:;" title="删除" onclick="delTr(this)"><i class="layui-icon">&#xe640;</i></a><input type="hidden" name="${inputName}[]" value="${id}"></td>`;

    tr += '</tr>';

    $(elem).append(tr);
}

/**
 * 删除行
 *
 * @param obj   当前td元素
 */
function delTr(obj) {
    $(obj).parents("tr").remove();
}

/**
 * 禁止退格键回退页面
 *
 * @param e
 * @returns {boolean}
 */
function forbidBackSpace(e) {
    // 获取event对象
    var ev = e || window.event;
    // 获取事件源(
    var obj = ev.target || ev.srcElement;
    // 获取事件源类型
    var t = obj.type || obj.getAttribute('type');
    // 获取作为判断条件的事件类型
    var vReadOnly = obj.readOnly;
    var vDisabled = obj.disabled;
    // 处理undefined值情况
    vReadOnly = (vReadOnly == undefined) ? false : vReadOnly;
    vDisabled = (vDisabled == undefined) ? true : vDisabled;
    // 当敲Backspace键时，事件源类型为密码或单行、多行文本的，
    // 并且readOnly属性为true或disabled属性为true的，则退格键失效
    var flag1 = ev.keyCode == 8 && (t == "password" || t == "text" || t == "textarea") && (vReadOnly == true || vDisabled == true);
    // 当敲Backspace键时，事件源类型非密码或单行、多行文本的，则退格键失效
    var flag2 = ev.keyCode == 8 && t != "password" && t != "text" && t != "textarea";
    // 判断
    if (flag1 || flag2) {
        return false;
    }
}

/**
 * 计算图片在容器最大可缩放大小
 *
 * @param percentage [zoom: "缩放比例, 0~1", max: "最大不可超过比例, 0~1", step: "超出每次扣减比例,重新计算, 0~1"]
 * @param params 单个参数为图片元素, 多个参数则为图片高宽度,窗口高宽度
 * @returns {{width: number, height: *}|*}
 */
function imgContainerZoom(percentage, ...params) {
    if (params.length === 0 || params.length > 2) throw "参数无效";

    // 手动获取图片的高宽度及可视商品高宽度
    let img, cli;
    if (params.length === 1) {
        img = {w: params[0].naturalWidth, h: params[0].naturalHeight};
        cli = {w: document.documentElement.clientWidth, h: document.documentElement.clientHeight};
    } else {
        [img, cli] = params;
    }

    // 缩放
    function zoom(cli, img, percentage) {
        let proportion = (cli.w * percentage.zoom - img.w) / img.w;
        let {width, height} = {width: cli.w * percentage.zoom, height: img.h * proportion + img.h};

        if (cli.h * percentage.max < height) {
            return zoom(cli, img, {...percentage, zoom: percentage.zoom - percentage.step});
        }

        return {width: width, height: height};
    }

    return zoom(cli, img, percentage);
}

/**
 * 倒计时
 *
 * @param time
 * @param fn 自定义执行函数
 * @returns {Promise<unknown>}
 */
function countdown(time, fn) {
    return new Promise((resolve, reject) => {
        let timer = setInterval(() => {
            if (--time === 0) {
                clearInterval(timer);
                return resolve();
            }

            if (fn) fn(time);
        }, 1000);
    });
}
